// This file is part of OpenMeca, an easy software to do mechanical simulation.
//
// Author(s)    :  - Damien ANDRE  <openmeca@gmail.com>
//
// Copyright (C) 2012 Damien ANDRE
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef OpenMeca_Core_AutoRegisterdPtr_hpp
#define OpenMeca_Core_AutoRegisterdPtr_hpp



namespace OpenMeca
{
  namespace Core
  {


    template<class Ptr, class Registered>
    class AutoRegisteredPtr
    {
    public:
      typedef Ptr PtrType;
      typedef Registered RegisteredType;


    public:
      AutoRegisteredPtr();
      AutoRegisteredPtr(Registered&);
      AutoRegisteredPtr(Registered&, Ptr&);
      ~AutoRegisteredPtr();
      Ptr* operator->();
      Ptr* operator=(Ptr*);
      AutoRegisteredPtr<Ptr, Registered>& operator=(const AutoRegisteredPtr<Ptr, Registered> &);
      Ptr* GetPtr();
      const Ptr* GetPtr() const;
      void Unregister();
      void SetNullPtr();

      

    private:

      friend class boost::serialization::access;
      template<class Archive> void save(Archive & ar, const unsigned int) const;
      template<class Archive> void load(Archive & ar, const unsigned int);
      BOOST_SERIALIZATION_SPLIT_MEMBER() 


    private:
      Ptr* pt_;
      Registered* object_;
      bool isCopy_;
    };


    template<class Ptr, class Registered>
    template<class Archive>
    inline void
    AutoRegisteredPtr<Ptr, Registered>::save(Archive & ar, const unsigned int) const
    {
      ar << BOOST_SERIALIZATION_NVP(pt_);
      ar << BOOST_SERIALIZATION_NVP(object_);
    }

    template<class Ptr, class Registered>
    template<class Archive>
    inline void
    AutoRegisteredPtr<Ptr, Registered>::load(Archive & ar, const unsigned int)
    {
      Unregister();
      ar >> BOOST_SERIALIZATION_NVP(pt_);
      ar >> BOOST_SERIALIZATION_NVP(object_);
      if (pt_->GetDependentItems().Contain(*object_) == false)
	pt_->AddChildItem(*object_);
    }


    template<class Ptr, class Registered>
    inline
    AutoRegisteredPtr<Ptr, Registered>::AutoRegisteredPtr(Registered& object)
      :pt_(0),
       object_(&object)
    {
    }

    template<class Ptr, class Registered>
    inline
    AutoRegisteredPtr<Ptr, Registered>::AutoRegisteredPtr(Registered& object, Ptr& pt)
      :pt_(0),
       object_(&object)
    {
      (*this)=&pt;
    }

    
    template<class Ptr, class Registered>
    inline
    AutoRegisteredPtr<Ptr, Registered>::~AutoRegisteredPtr()
    {
      if (pt_->GetDependentItems().Contain(*object_) == true)
	Unregister();
    }
    

    template<class Ptr, class Registered>
    inline void
    AutoRegisteredPtr<Ptr, Registered>::Unregister()
    {
      if (pt_ != 0 && object_ !=0)
	{
	  pt_->EraseChildItem(*object_);
	  pt_ = 0;
	}
    }

    template<class Ptr, class Registered>
    inline void
    AutoRegisteredPtr<Ptr, Registered>::SetNullPtr()
    {
      pt_ = 0;
    }
    
    template<class Ptr, class Registered>
    inline Ptr*
    AutoRegisteredPtr<Ptr, Registered>::operator->()
    {
      assert(pt_ != 0);
      return pt_;
    }
    
    
    template<class Ptr, class Registered>
    inline Ptr*
    AutoRegisteredPtr<Ptr, Registered>::operator=(Ptr* pt)
    {
      if (pt_ != 0)
	pt_->EraseChildItem(*object_);
    
      pt_ = pt;
    
      if (pt_ != 0)
	pt_->AddChildItem(*object_);

      return pt_;
    }

    template<class Ptr, class Registered>
    inline AutoRegisteredPtr<Ptr, Registered>&
    AutoRegisteredPtr<Ptr, Registered>::operator=(const AutoRegisteredPtr<Ptr, Registered> & apt)
    {
      object_ = apt.object_;
      pt_ = apt.pt_;
      return *this;
	
    }



    template<class Ptr, class Registered>
    inline Ptr*
    AutoRegisteredPtr<Ptr, Registered>::GetPtr()      
    {
      return pt_;
    }

    template<class Ptr, class Registered>
    inline const Ptr*
    AutoRegisteredPtr<Ptr, Registered>::GetPtr() const
    {
      return pt_;
    }


    template<class Ptr, class Registered>
    inline
    AutoRegisteredPtr<Ptr, Registered>::AutoRegisteredPtr()
      :pt_(0),
       object_(0)
    {
    }


    
  
  }
}


#endif
